package com.atom.app.mvi.ui.main.f2

import android.os.Parcelable
import com.atom.app.mvi.ui.main.UiEvent
import com.atom.app.mvi.ui.main.f2.Feature2.News
import com.atom.app.mvi.ui.main.f2.Feature2.State
import com.atom.module.mvi.android.AndroidMainThreadFeatureScheduler
import com.atom.module.mvi.core.element.*
import com.atom.module.mvi.core.feature.ActorReducerFeature
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Observable.just
import kotlinx.parcelize.Parcelize

sealed class Effect {
    object None : Effect()
    object StartedLoading : Effect()
    data class LoadedImage(val url: String) : Effect()
    data class ErrorLoading(val throwable: Throwable) : Effect()
}

class Feature2(
    timeCapsule: TimeCapsule<Parcelable>? = null
) : ActorReducerFeature<UiEvent, Effect, State, News>(
    initialState = timeCapsule?.get(Feature2::class.java) ?: State(),// state 提供者 用于数据的记录恢复以及初始化
    bootstrapper = BootStrapperImpl(), // 事件发生器,用于初始化界面,例如刚进入App某个界面,需要对界面进行简单的数据请求等操作
    actor = ActorImpl(), // 时间消费转换器,用于将Wish/Action 转为 中间的Effect
    reducer = ReducerImpl(),// Effect 进行解析,并生成State , 之前Feature1 直接使用UIEvent (Wish 作为state构造的属性提供对象)
    newsPublisher = NewsPublisherImpl(), //消息的推送
    featureScheduler = AndroidMainThreadFeatureScheduler
) {
    init {
        // 初始化state
        timeCapsule?.register(Feature2::class.java) {
            state.copy(
                isLoading = false
            )
        }
    }

    @Parcelize
    data class State(
        val isLoading: Boolean = false,
        val imageUrl: String? = null
    ) : Parcelable

    class BootStrapperImpl : Bootstrapper<UiEvent> {
        override fun invoke(): Observable<out UiEvent> = just(UiEvent.ImageClicked)
    }

    class ActorImpl : Actor<State, UiEvent, Effect> {
        private val service = CatApi.service

        override fun invoke(state: State, wish: UiEvent): Observable<Effect> = when (wish) {
            is UiEvent.ImageClicked -> loadRandomImage().map { Effect.LoadedImage(it.url!!) as Effect }
                .startWith(just(Effect.StartedLoading))
                .onErrorReturn { Effect.ErrorLoading(it) }
            else -> {
                just(Effect.None)
            }
        }

        fun loadRandomImage(): Observable<Response> {
            return service.getRandomImage().randomlyThrowAnException()
        }
    }

    class ReducerImpl : Reducer<State, Effect> {
        override fun invoke(state: State, effect: Effect): State = when (effect) {
            Effect.StartedLoading -> state.copy(
                isLoading = true
            )
            is Effect.LoadedImage -> state.copy(
                isLoading = false, imageUrl = effect.url
            )
            is Effect.ErrorLoading -> state.copy(
                isLoading = false
            )
            else -> {
                state
            }
        }
    }


    sealed class News {
        data class ErrorExecutingRequest(val throwable: Throwable) : News()
    }

    class NewsPublisherImpl : NewsPublisher<UiEvent, Effect, State, News> {
        override fun invoke(wish: UiEvent, effect: Effect, state: State): News? = when (effect) {
            is Effect.ErrorLoading -> News.ErrorExecutingRequest(effect.throwable)
            else -> null
        }
    }
}
